package org.vpm.mediaplayer;

import java.io.IOException;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;

public class PlayerService extends Service {
	
	private MediaPlayer mp = new MediaPlayer();
	private boolean mActive = false, mRemote;
	private NotificationManager nm;
	private static final int PLAYING_ID = 0;
	private RemoteCallbackList<IPlayerServiceCallback> mCallbacks = new RemoteCallbackList<IPlayerServiceCallback>();
	private String mErrorString = "";
	
	
	public final static int PREPARED = 0;
	public final static int UPDATE = 1;
	public final static int BUFFERING = 2;
	public final static int FINISHED = 3;
	public final static int ERROR = 4;
	public final static int COMPLETION = 5;
	public final static int callbackCount = 6;
	
	Handler mUpdateHandler = new Handler() {
		public void handleMessage(Message msg) {
			switch(msg.what) {
				case UPDATE:
					processCallbacks(UPDATE, mp.getCurrentPosition());
					mUpdateHandler.sendMessageDelayed(obtainMessage(UPDATE), 1000);
				default:
					super.handleMessage(msg);
			}
		}
	};
	
	private void setActive(boolean active) {
		mActive = active;
		if(active) {
			Notification notification = new Notification(R.drawable.icon, null, System.currentTimeMillis());
			notification.tickerText = String.format(getResources().getString(R.string.player_playing), "Title");
			notification.icon = R.drawable.icon;
			notification.flags = Notification.FLAG_NO_CLEAR;
			PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, PlayerActivity.class), 0);
			notification.setLatestEventInfo(this, "VPM Media", String.format(getResources().getString(R.string.player_playing), "Title"), contentIntent);
			nm.notify(PLAYING_ID, notification);
			processCallbacks(PREPARED, 0);
		} else {
			nm.cancel(PLAYING_ID);
		}
	}
	
	@Override
	public void onCreate() {
		super.onCreate();
		nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
		mp.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
			public void onPrepared(MediaPlayer mp) {
				setActive(true);
			}
		});
		mp.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
			public void onBufferingUpdate(MediaPlayer mp, int percent) {
				processCallbacks(BUFFERING, percent * mp.getDuration() / 100);
			}
		});
		mp.setOnErrorListener(new MediaPlayer.OnErrorListener() {
			public boolean onError(MediaPlayer mp, int what, int extra) {
				processCallbacks(ERROR, what);
				return true;
			}
		});
		mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
			public void onCompletion(MediaPlayer mp) {
				stopUpdates();
				setActive(false);
				processCallbacks(COMPLETION, 0);
			}
		});
	}
	
	@Override
	public IBinder onBind(Intent intent) {
		return mBinder;
	}
	
	@Override
	public void onDestroy() {
		mp.stop();
		mp.release();
		mCallbacks.kill();
	}
	
	private void processCallbacks(int type, int value) {
		final int N = mCallbacks.beginBroadcast();
		for(int i=0; i<N; i++) {
			try {
				mCallbacks.getBroadcastItem(i).update(type, value);
			} catch (RemoteException e) {
				// RemoteCallbackList will take care of GC
			}
		}
	}
	
	public void start() {
		mp.start();
		startUpdates();
	}
	
	public void pause() {
		stopUpdates();
		mp.pause();
	}
	
	public void stop() {
		stopUpdates();
		mp.stop();
		setActive(false);
	}
	
	public void seekTo(int progress) {
		mp.seekTo(progress);
	}
	
	public int duration() {
		return mp.getDuration();
	}
	
	public int currentPosition()
	{
		return mp.getCurrentPosition();
	}
	
	public boolean playing() {
		return mp.isPlaying();
	}
	
	public boolean remotePlay() {
		try {
			mp.reset();
			mp.setDataSource("http://www.vpm.org/files/Mike%20Folder/Podcasts/02-21-10%20Text%20-%20Ding!.mp3");
			mp.prepareAsync();
			mRemote = true;
			return true;
		} catch (IllegalArgumentException e) {
			Log.e("Player Services", "Illegal Argument", e);
		} catch (SecurityException e) {
			Log.e("Player Services", "Security Exception", e);
		} catch (IllegalStateException e) {
			Log.e("Player Services", "Illegal State", e);
		} catch (IOException e) {
			Log.e("Player Services", "IO Exception", e);
		}
		
		return false;
	}
	
	private void startUpdates() {
		mUpdateHandler.sendEmptyMessage(UPDATE);
	}
	
	private void stopUpdates() {
		mUpdateHandler.removeMessages(UPDATE);
	}
	
	
	private final IPlayerService.Stub mBinder = new IPlayerService.Stub() {
		
		@Override
		public void unregisterCallback(IPlayerServiceCallback cb)
				throws RemoteException {
			if (cb != null) mCallbacks.unregister(cb);
		}
		
		@Override
		public void stopTrack() throws RemoteException {
			stop();
		}
		
		@Override
		public void startTrack() throws RemoteException {
			start();
		}
		
		@Override
		public void seekTrack(int progress) throws RemoteException {
			seekTo(progress);
		}
		
		@Override
		public void registerCallback(IPlayerServiceCallback cb)
				throws RemoteException {
			if(cb != null) mCallbacks.register(cb);
		}
		
		@Override
		public void pauseTrack() throws RemoteException {
			pause();
		}
		
		@Override
		public boolean isRemote() throws RemoteException {
			return mRemote;
		}
		
		@Override
		public boolean isPlaying() throws RemoteException {
			return playing();
		}
		
		@Override
		public boolean isActive() throws RemoteException {
			return mActive;
		}
		
		@Override
		public String getError() throws RemoteException {
			return mErrorString;
		}
		
		@Override
		public int getDuration() throws RemoteException {
			return duration();
		}
		
		@Override
		public int getCurrentPosition() throws RemoteException {
			return currentPosition();
		}
		
		@Override
		public boolean remoteTrack() throws RemoteException {
			return remotePlay();
		}
	};

}
